﻿// ***********************************************************************
// Provided for Informational Purposes Only
//
// Apache 2.0 License
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY 
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR 
// PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
// ***********************************************************************
// Assembly  : ContractMeow.Api.WinRT
// File      : HttpRequestObject.cs
//
// ***********************************************************************
using ContractMeow.WinRT.Extensions;
using ContractMeow.WinRT.Serialization;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace ContractMeow.Api.Net
{
	internal class HttpRequestObject
	{
		private IAuthenticator _authenticator;
		private IQueryStringSerializer _queryStringSerializer;
		private ISerializer _serializer;

		public HttpRequestObject(IAuthenticator authenticator,
			IQueryStringSerializer queryStringSerializer,
								 ISerializer serializer)
		{
			_authenticator = authenticator;
			_queryStringSerializer = queryStringSerializer;
			_serializer = serializer;
		}

		protected virtual HttpRequestMessage CreateRequestMessage(Uri baseUri, string uri, HttpMethod method, Object parameters)
		{
			var uriBuilder = new UriBuilder(
						uri.IsUri()
							? uri
							: baseUri + uri);

			if (IncludeParametersInUri(method))
			{
				// We're not doing parallel serialization here, so await could be more costly than simply
				// calling for the result synchronously.
				var queryString = _queryStringSerializer.SerializeAsync(parameters).Result;
				if (!string.IsNullOrEmpty(queryString))
				{
					uriBuilder.Query = String.Format("{0}{1}{2}",
													 uriBuilder.Query,
													 uriBuilder.Query.Contains("?") ? "&" : String.Empty,
													 queryString);
				}
			}

			var request = new HttpRequestMessage
				{
					Method = method,
					RequestUri = uriBuilder.Uri,
				};
			if (!IncludeParametersInUri(method))
			{
				var serializedContent = _serializer.SerializeToByteArray(parameters);
				request.Content = new ByteArrayContent(serializedContent);
			}

			if (request.Content != null)
				request.Content.Headers.ContentType = new MediaTypeHeaderValue(_serializer.ContentType);

			return request;
		}

		/// <summary>
		/// Submits request to the API synchronously.
		/// This method was created to replace the lambda expression as part of initializing
		/// the work item in ExecuteAsync (readability)
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="client">The client.</param>
		/// <param name="method">The method.</param>
		/// <param name="baseUri">The base URI.</param>
		/// <param name="uri">The URI.</param>
		/// <param name="parameters">The parameters.</param>
		/// <returns>Task{IBaseEnvelope{``0}}.</returns>
		public virtual async Task<HttpResponseMessage> ExecuteRequestAsync<T>(HttpClient client,
																			 HttpMethod method,
																			 Uri baseUri, string uri,
																			 Object parameters) where T : class
		{
			HttpResponseMessage response = null;
			using (HttpRequestMessage request = CreateRequestMessage(baseUri, uri, method, parameters))
			{
				if (_authenticator != null)
					_authenticator.Authenticate(client);

				if (request != null)
				{
					response = await client.SendAsync(request);
				}
			}
			return response;
		}

		/// <summary>
		/// Checks if parameters need to be included in the URI (only for GET)
		/// </summary>
		/// <param name="method">The method.</param>
		/// <returns>Boolean.</returns>
		private Boolean IncludeParametersInUri(HttpMethod method)
		{
			return method != HttpMethod.Post
				   && method != HttpMethod.Put
				   && method != HttpMethod.Delete;
		}
	}
}
